package com.winit.schedule.job.tracker.schedule;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.util.StringUtils;

import com.winit.schedule.core.model.AssignedTask;
import com.winit.schedule.core.model.TaskID;
import com.winit.schedule.core.repository.constants.TaskStatus;
import com.winit.schedule.core.repository.entity.TaskEntity;
import com.winit.schedule.core.repository.entity.TaskItemEntity;
import com.winit.schedule.core.repository.entity.TaskRunnerEntity;
import com.winit.schedule.core.repository.entity.TaskTrackerEntity;
import com.winit.schedule.core.task.ScheduleTaskDefine;
import com.winit.schedule.core.utils.Clock;
import com.winit.schedule.core.utils.CronExpression;
import com.winit.schedule.job.tracker.JobTrackerContext;
import com.winit.schedule.job.tracker.repository.ScheduleDataManager;

/**
 * 默认任务调度器 以队列方式向TaskTracker分配任务项；对于单个任务，每次为每个TaskTracker分配一个任务项
 * 
 * @author jianke.zhang 2015-4-16 下午6:17:23
 * @since 1.0
 */
public class DefaultTaskManager implements TaskScheduleManager {

    private static final transient Logger logger = LoggerFactory.getLogger(DefaultTaskManager.class);

    private JobTrackerContext             jobTrackerContext;

    private ScheduleDataManager           scheduleDataManager;

    public DefaultTaskManager(JobTrackerContext jobTrackerContext){
        this.jobTrackerContext = jobTrackerContext;
        this.scheduleDataManager = jobTrackerContext.getScheduleDataManager();
    }

    @Override
    public List<AssignedTask> getExecutableTask(TaskTrackerEntity taskTracker, List<TaskRunnerEntity> runners)
                                                                                                              throws Exception {
        Date currentDate = Clock.getCurrentDate();
        List<AssignedTask> tasks = getRequestTask(taskTracker, runners);
        List<AssignedTask> executableTasks = new ArrayList<AssignedTask>();
        TaskEntity taskEntity = null;
        TaskItemEntity taskItem = null;
        for (AssignedTask task : tasks) {
            String taskId = task.getTaskId().getTaskId();
            taskEntity = scheduleDataManager.getTaskEntityById(taskId);
            if (null != taskEntity && TaskStatus.START.equals(taskEntity.getTaskStatus())
                && allowIpToTaskTracker(taskEntity, taskTracker)) {
                BeanUtils.copyProperties(taskEntity, task);
                computerRunTime(currentDate, task);

                //String lock = String.valueOf(taskId);
                //synchronized (lock) {
                    taskItem = scheduleDataManager.findAndFetchTaskItemByTaskId(taskTracker.getTaskTrackerUuid(),
                        taskId,
                        task.getPermitRunStartTime(),
                        task.getCurrentFetchNum());
                //}
                // 如果抓取到可执行任务项
                if (null != taskItem) {
                    task.getTaskItems().add(taskItem);
                    task.setPermitRunStartTime(taskItem.getNextRunTime());
                    executableTasks.add(task);
                }
            } else {
                if (logger.isDebugEnabled()) {
                    logger.debug("No query to task : " + taskId);
                }
            }
        }
        return executableTasks;
    }

    private boolean allowIpToTaskTracker(TaskEntity taskEntity, TaskTrackerEntity taskTracker) {
        String ipstr = StringUtils.trimAllWhitespace(taskEntity.getIpWhiteList());
        ;
        if (null == ipstr || ipstr.trim() == "") {
            return true;
        }

        String[] ipArr = StringUtils.delimitedListToStringArray(ipstr, ",");
        if (null != ipArr) {
            for (String ip : ipArr) {
                if (taskTracker.getIp().equals(ip)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 计算任务允许运行的开始时间和结束时间
     * 
     * @param currentDate
     * @param task
     * @throws Exception
     */
    private void computerRunTime(Date currentDate, AssignedTask task) throws Exception {

        if (null != task.getRunStartTime()) {
            CronExpression startTime = new CronExpression(task.getRunStartTime());
            Date firstStartTime = startTime.getNextValidTimeAfter(currentDate);
            long permitRunEndTime = 0;
            if (null != task.getRunEndTime() && !task.getRunEndTime().trim().equals("")) {
                String tmpEndStr = task.getRunEndTime();
                CronExpression cexpEnd = new CronExpression(tmpEndStr);
                Date firstEndTime = cexpEnd.getNextValidTimeAfter(firstStartTime);
                Date nowEndTime = cexpEnd.getNextValidTimeAfter(currentDate);
                if (!nowEndTime.equals(firstEndTime) && currentDate.before(nowEndTime)) {
                    firstEndTime = nowEndTime;
                }
                permitRunEndTime = firstEndTime.getTime();
            }

            if (null != firstStartTime) {
                task.setPermitRunStartTime(firstStartTime.getTime());
            } else {
                task.setPermitRunStartTime(-1);
            }
            task.setPermitRunEndTime(permitRunEndTime);
        } else {
            task.setPermitRunStartTime(Clock.getSystemTime());
        }

    }

    /**
     * 获取taskTracker已定义且还未执行的任务
     * 
     * @param taskTracker
     * @param runners
     * @return
     */
    private List<AssignedTask> getRequestTask(TaskTrackerEntity taskTracker, List<TaskRunnerEntity> runners) {
        List<AssignedTask> tasks = new ArrayList<AssignedTask>();
        AssignedTask task = null;
        boolean isRunning = false;
        for (ScheduleTaskDefine define : taskTracker.getTaskDefines()) {
            if (!define.isExclude()) {
                for (TaskRunnerEntity runner : runners) {
                    if (define.getTaskId().equals(runner.getTaskId().getTaskId())) {
                        isRunning = true;
                    }
                }
                if (!isRunning) {
                    task = new AssignedTask();
                    task.setTaskDefine(define);
                    task.setTaskId(new TaskID(null, define.getTaskId()));
                    tasks.add(task);
                }
            }

        }

        return tasks;
    }

    public JobTrackerContext getJobTrackerContext() {
        return jobTrackerContext;
    }

}
